package com.nwpu.controller;

import com.nwpu.common.ErrorEnum;
import com.nwpu.common.Result;
import com.nwpu.domain.Goods;
import com.nwpu.domain.Order;
import com.nwpu.domain.SecKillGoods;
import com.nwpu.service.GoodsService;
import com.nwpu.service.OrderService;
import com.nwpu.util.RedisUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

/**
 * @author DPH
 * @date 2023/5/26
 */
@RestController
@RequestMapping("/seckill")
public class SeckillController {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private GoodsService goodsService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private RedissonClient redissonClient;

    @GetMapping("/{path}")
    public Result<String> verifyPath(@PathVariable String path) {
        // 验证path是否存在，存在则返回value
        String value = (String) redisUtil.get(path);
        if (value == null) {
            return Result.error(ErrorEnum.DATA_NOT_EXIST);
        }
        return Result.of(value);
    }

    @PostMapping("/doSeckill3")
    public Result<Boolean> doSeckill3(@RequestBody Order order, Authentication authentication) {
        if (authentication == null || !authentication.isAuthenticated()) {
            return Result.error(ErrorEnum.FORBIDDEN);
        }

        String goodsId = order.getGoodsId();
        String userId = authentication.getName();
        // 验证商品是否已经开始秒杀
        if (!checkStart(goodsId)) {
            return Result.error(ErrorEnum.SECKILL_NOT_START);
        }
        // 验证用户是否重复抢购
        if (checkMultipleBuying(goodsId, userId)) {
            return Result.error(ErrorEnum.REPEAT_SECKILL);
        }

        // 扣减库存
        RLock lock = redissonClient.getLock("REDIS_DISTR_LOCK_" + goodsId);
        lock.lock();
        try {
            if (checkMultipleBuying(goodsId, userId)) {
                return Result.error(ErrorEnum.REPEAT_SECKILL);
            }
            // 验证库存是否充足
            if (!checkStock(goodsId)) {
                return Result.error(ErrorEnum.STOCK_NOT_ENOUGH);
            }
            SecKillGoods goods = (SecKillGoods) redisUtil.get("GOODS_" + goodsId);
            goods.setInventory(goods.getInventory() - order.getPurchaseNum());
            redisUtil.set("GOODS_" + goodsId, goods);
        } finally {
            lock.unlock();
        }
        // 生成订单
        String key = "USER_HAS_SECKILL_" + authentication.getName() + "_" + goodsId;
        redisUtil.set(key, "1");
        return Result.of(orderService.saveOrder(order));
    }

    @PostMapping("/doSeckill1")
    public Result<Boolean> doSeckill1(@RequestBody Order order, Authentication authentication) {
        String goodsId = order.getGoodsId();
        String userId = authentication.getName();
        // 验证商品是否已经开始秒杀
        if (!checkStart(goodsId)) {
            return Result.error(ErrorEnum.SECKILL_NOT_START);
        }
        // 验证用户是否重复抢购
        if (checkMultipleBuying(goodsId, userId)) {
            return Result.error(ErrorEnum.REPEAT_SECKILL);
        }
        // 验证库存是否充足
        if (!checkStock(goodsId)) {
            return Result.error(ErrorEnum.STOCK_NOT_ENOUGH);
        }
        SecKillGoods goods = (SecKillGoods) redisUtil.get("GOODS_" + goodsId);
        goods.setInventory(goods.getInventory() - order.getPurchaseNum());
        redisUtil.set("GOODS_" + goodsId, goods);
        // 生成订单
        String key = "USER_HAS_SECKILL_" + authentication.getName() + "_" + goodsId;
        redisUtil.set(key, "1");
        return Result.of(orderService.saveOrder(order));
    }

    @PostMapping("/doSeckill2")
    public Result<Boolean> doSeckill2(@RequestBody Order order, Authentication authentication) {
        String goodsId = order.getGoodsId();
        String userId = authentication.getName();
        // 验证商品是否已经开始秒杀
        if (!checkStart(goodsId)) {
            return Result.error(ErrorEnum.SECKILL_NOT_START);
        }
        // 扣减库存
        RLock lock = redissonClient.getLock("REDIS_DISTR_LOCK_" + goodsId);
        lock.lock();
        try {
            if (checkMultipleBuying(goodsId, userId)) {
                return Result.error(ErrorEnum.REPEAT_SECKILL);
            }
            // 验证库存是否充足
            if (!checkStock(goodsId)) {
                return Result.error(ErrorEnum.STOCK_NOT_ENOUGH);
            }
            SecKillGoods goods = (SecKillGoods) redisUtil.get("GOODS_" + goodsId);
            goods.setInventory(goods.getInventory() - order.getPurchaseNum());
            redisUtil.set("GOODS_" + goodsId, goods);
        } finally {
            lock.unlock();
        }
        // 生成订单
        String key = "USER_HAS_SECKILL_" + authentication.getName() + "_" + goodsId;
        redisUtil.set(key, "1");
        return Result.of(orderService.saveOrder(order));
    }

    @PostMapping("/doSeckill4")
    public Result<Boolean> doSeckill4(@RequestBody Order order, Authentication authentication) {
        String goodsId = order.getGoodsId();
        String userId = authentication.getName();
        // 验证商品是否已经开始秒杀
        if (!checkStart(goodsId)) {
            return Result.error(ErrorEnum.SECKILL_NOT_START);
        }
        // 扣减库存
        String key = "GOODS_INVENTORY" + goodsId;
        long value = -1;
        if (checkStock(goodsId)) {
            value = redisUtil.decr(key, order.getPurchaseNum());
        } else {
            return Result.error(ErrorEnum.STOCK_NOT_ENOUGH);
        }

        if (value >= 0) {
            RLock lock = redissonClient.getLock("REDIS_DISTR_LOCK_" + goodsId);
            lock.lock();
            try {
                if (checkMultipleBuying(goodsId, userId)) {
                    return Result.error(ErrorEnum.REPEAT_SECKILL);
                }
                String buyKey = "USER_HAS_SECKILL_" + userId + "_" + goodsId;
                long currentTime = new Date().getTime();
                SecKillGoods goods = (SecKillGoods) redisUtil.get("GOODS_" + goodsId);
                long endTime = goods.getEndTime().getTime();
                redisUtil.set(buyKey, "1", endTime - currentTime);
            } finally {
                lock.unlock();
            }
            // 生成订单
            boolean flag = orderService.saveOrder2(order);
            if (flag) {
                return Result.of(true);
            } else {
                redisUtil.incr(key, order.getPurchaseNum());
                return Result.error(ErrorEnum.INTERNAL_SERVER_ERROR);
            }
        } else {
            redisUtil.incr(key, order.getPurchaseNum());
            return Result.error(ErrorEnum.STOCK_NOT_ENOUGH);
        }
    }

    private boolean checkStart(String goodsId) {
        Goods goods = (Goods) redisUtil.get("GOODS_" + goodsId);
        if (goods == null) {
            return false;
        }
        if (goods.getBeginTime().after(new Date())) {
            return false;
        }
        return true;
    }

    private boolean checkMultipleBuying(String goodsId, String userId) {
        String key = "USER_HAS_SECKILL_" + userId + "_" + goodsId;
        if (redisUtil.get(key) != null) {
            return true;
        }
        return false;
    }

    //判断库存是否充足
    private boolean checkStock(String goodsId) {
        String key = "GOODS_" + goodsId;
        SecKillGoods goods = (SecKillGoods) redisUtil.get(key);
        if (goods.getInventory() <= 0) {
            return false;
        }
        return true;
    }

    private boolean checkInventory(String goodsId) {
        String key = "GOODS_INVENTORY" + goodsId;
        int inventory = (int) redisUtil.get(key);
        if (inventory <= 0)
            return false;
        return true;
    }
}
